﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Reflection;
using System.Runtime.Serialization;
using ArtMath.Core.Data;
using System.Windows.Forms;
using ArtMath.Control;
using ArtMath.Parameter;
using ArtMath.Attribute;
using ArtMath.CommonType;
using ArtMath.Data;
using ArtMath.Component.Manager;
using ArtMath.Resource;
using ArtMath.Core.Helper;
using ArtMath.Editor;
using ArtMath.AuxiliaryTool;
using ArtMath.Flag;
using ArtMath.Setting;
using ArtMath.Component.Point;
using ArtMath.Component.Annotation;

namespace ArtMath.Component
{
    public enum ToolTypeName
    {
        Unknown,
        qly,
        ysxl,
        ysty,
        yssqx,
        yspwx,
        jzbsx,
        zz,
        ysj,
        sdjd,
        tlgs,
        bjyxjsx,
        xggx,
        xz,
        yz,
        jz,
        sf,
        cxd,
        ydhb,
        yddx,
        qhdlx,
        yzb,
        ygb,
        sz,
        dxp,
        qyxp,
        wb,
        zjzbx,
        jzbx,
        zyd,
        zbd,
        jd,
        jdglq,
        dbfd,
        zd,
        hs,
        csfc,
        ldxd,
        yxysxd,
        ldsx,
        ldzx,
        yszx,
        yssx,
        jdfsx,
        cx,
        zcx,
        pxx,
        qx,
        gqx,
        qxglq,
        rydbx,
        zjsjx,
        dysjx,
        dyzjsjx,
        dbsjx,
        pxsbx,
        jx,
        lx,
        zfx,
        tx,
        dytx,
        zjtx,
        zdbx,
        yxddy,
        yxbjy,
        zjy,
        sdy,
        sdyh,
        sdsx,
        sdgx,
        cdzty,
        jdddty,
        jdzxty,
        sxzsqx,
        jdddsqx,
        jdszsqx,
        jdxzsqx,
        jdzxsqx,
        jdzxpwx,
        gjqxfczqx,
        ldxl,
        pxxl,
        czxl,
        xljf,
        xljf_,
        ldjl,
        zc,
        mj,
        lzxjj,
        lzxdj,
        tp,
        ddzxdjl,
        lzxjl,
        lxldslj,
        lxldxlj,
        zdc,
        zxdc,
        dbxzx,
        sjxwx,
        sjxnx,
        sjxcx,
        wjy,
        nqy,
        pqy,
        dhs,
        yhs,
        jpfx,
        jndfx,
        zx,
        zybh,
    }
    [Serializable]//Exception代码模板自带的
    public class ComponentCreatingException : Exception
    {
        [Browsable(false)]
        public ShapeBase FailShape { get; }
        public ComponentCreatingException(ShapeBase failShape, string message) : base(message)
        {
            FailShape = failShape;
        }
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue(nameof(FailShape), FailShape);
            base.GetObjectData(info, context);
        }
    }
    //[PropertyTab(typeof(PropertyDisplayerPropertyTab), PropertyTabScope.Component)]
    [Serializable]
    public abstract class Base : IRecoverableDelete, IFormatPainter<Base>, ICopyPaste, ArtMath.CommonType.ISerializable
    {
        public static readonly PointD AnchorOffset = new PointD(5, 5);
        RPointS rotateCenter;
        AngleStringX rotateAngle;
        string name;
        PropertyDisplayer nameDisplayer;
        bool selected, creationCompleted, visible = true;
        [NonSerialized]
        protected ListViewItem recycleItem;
        [Browsable(false)]
        public ListViewItem RecycleItem { get { return recycleItem; } }
        [Browsable(false)]
        public ObjectState ObjectState { get; set; } = ObjectState.Normal;
        public StyleState StyleState = StyleState.Normal;
        public PointD SpareCoordinate;
        [Browsable(false)]
        public abstract PointD MassPoint { get; }
        [Browsable(false)]
        public SketchPad Pad { get; }
        [Browsable(false)]
        public LayerItem LayerItem { get; protected set; }
        [Browsable(false)]
        public ToolTypeName ToolType { get; }
        /// <summary>
        /// 获取管理此组件的组件管理器集合
        /// </summary>
        [Browsable(false)]
        public List<ComponentManagerBase> ComponentManagers { get; } = new List<ComponentManagerBase>();
        [Browsable(false)]
        public LabelLocationManager PropertiesLocationManager { get; } = new LabelLocationManager(new RectangleD(0, 0, double.PositiveInfinity, double.PositiveInfinity));
        [Browsable(false)]
        public List<ObjectParameter> ObjectParameters { get; } = new List<ObjectParameter>();
        [Browsable(false)]
        public virtual bool IsCreationCompleted
        {
            get { return creationCompleted; }
        }
        public Base(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true)
        {
            Pad = pad;
            ToolType = toolType;
            NameDisplayer = new PropertyDisplayer(this, "Name");
            Name = name;
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate || ToolType == ToolTypeName.jzbx)
            {
                RotateCenter = new PPointS(Pad);
            }
            else
            {
                RotateCenter = new RPointS(Pad);
            }
            RotateAngle = new AngleStringX(Pad, 0);
            if (addToList)
            {
                LayerItem = new LayerItem(Name, this);
            }
        }
        public virtual void RotateCenter_CoordinateChanged(PointD oldValue, PointD newValue)
        {

        }

        public virtual void RotateAngleChanged(double oldValue)
        {

        }
        void RotateAngle_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            RotateAngleChanged(e.OldValue);
        }
        void RotateAngle_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            RotateAngleChanged(e.OldValue);
        }
        [Browsable(false)]
        public virtual bool Movable
        {
            get { return true; }
        }
        [Browsable(false)]
        public PointD SpareCoordinate_
        {
            get { return SpareCoordinate; }
            set
            {
                SpareCoordinate = value;
                ComponentBuilding(this);
            }
        }
        [Browsable(false)]
        public virtual bool IsIndependent
        {
            get
            {
                return ComponentManagers.Count == 0;
            }
        }
        [Browsable(false)]
        public virtual bool CanDrawVirtual
        {
            get { return true; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "Name", "Displayer")]
        public PropertyDisplayer NameDisplayer
        {
            get { return nameDisplayer; }
            set { nameDisplayer = value; }
        }
        public abstract PointD AnchorPoint(PropertyDisplayer disp);
        [MultilingualCategory("Basic"), MultilingualDisplayName("Name")]
        public virtual string Name
        {
            get { return name; }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    Pad.ShowMessage(Resources.Translate("NameCannotBeEmpty"),  MessageType.Exclamation);
                }
                else
                {
                    if (LayerItem != null && Pad.CheckObjectNameExist(value))
                    {
                        if (MessageBox.Show(string.Format(Resources.Translate("ComponentNameExistInSketchpad"), value), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                        {
                            value = Utility.GetNextAvailableName(value, Pad.CheckObjectNameExist);
                        }
                        else
                        {
                            return;
                        }
                    }
                    name = value;
                    if (LayerItem != null)
                    {
                        LayerItem.Name = name;
                    }
                    if (RecycleItem != null)
                    {
                        RecycleItem.SubItems[0].Text = name;
                    }
                    NameDisplayer.RefreshText();
                    Pad.Canvas.Invalidate();
                }
            }
        }
        [MultilingualCategory("View"), MultilingualDisplayName("Visible"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "True")]
        public virtual bool Visible
        {
            get { return visible; }
            set
            {
                visible = value;
                if (LayerItem != null)
                {
                    LayerItem.LinkedObjectVisible = visible;
                }
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualCategory("Transform"), DefaultValue(typeof(RPointS), "0, 0"), MultilingualDisplayName(null, "Rotate", "Center")]
        public virtual RPointS RotateCenter
        {
            get { return rotateCenter; }
            set
            {
                if (rotateCenter != null)
                {
                    rotateCenter.CoordinateChanged -= RotateCenter_CoordinateChanged;
                }
                rotateCenter = value;
                if (rotateCenter != null)
                {
                    rotateCenter.CoordinateChanged += RotateCenter_CoordinateChanged;
                }
            }
        }
        [MultilingualCategory("Transform"), MultilingualDisplayName(null, "Rotate", "Angle"), DefaultValue(typeof(AngleStringX), "0"), Editor(typeof(AngleEditor), typeof(UITypeEditor))]
        public virtual AngleStringX RotateAngle
        {
            get { return rotateAngle; }
            set
            {
                if (rotateAngle is object)
                {
                    rotateAngle.ParametersValueChanged -= RotateAngle_ParametersValueChanged;
                    rotateAngle.ExpressionSet -= RotateAngle_ExpressionSet;
                }
                rotateAngle = value;
                if (rotateAngle is object)
                {
                    rotateAngle.ParametersValueChanged += RotateAngle_ParametersValueChanged;
                    rotateAngle.ExpressionSet += RotateAngle_ExpressionSet;
                }
            }
        }
        [Browsable(false)]
        public virtual StyleState StyleState_
        {
            get { return StyleState; }
            set
            {
                StyleState = value;
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual bool Selected_
        {
            get { return selected; }
            set
            {
                selected = value;
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual bool Selected
        {
            get { return selected; }
            set
            {
                selected = value;
                if (selected)
                {
                    if (Pad.SelectedObjects.Add(this))
                    {
                    }
                }
                else
                {
                    if (Pad.SelectedObjects.Remove(this))
                    {
                    }
                }
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual bool IsTop
        {
            get { return true; }
        }
        [Browsable(false)]
        public virtual bool IsEntity
        {
            get { return true; }
        }
        [Browsable(false)]
        public virtual bool CanRecover
        {
            get { return ObjectState == ObjectState.Deleted; }
        }
        public virtual Base[] CheckNearBy(PointD Plocation)
        {
            return new Base[0];
        }
        public virtual PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            if (NameDisplayer.InRegion(Plocation))
            {
                return new PropertyDisplayer[] { NameDisplayer };
            }
            else
            {
                return new PropertyDisplayer[0];
            }
        }
        protected virtual HashSet<Base> getRelatedObjects()
        {
            return new HashSet<Base>(ComponentManagers);
        }
        public virtual object GetCopyInfo()
        {
            return null;
        }
        public virtual void AutoCreate(object createInfo, bool tryOffset = false)
        {
            CreateComponents();
        }
        public void AllRelatedObjectsIterator(Predicate<Base> act)
        {
            if (act == null) return;
            Queue<Base> Q = new Queue<Base>();
            HashSet<Base> flags = new HashSet<Base>();
            foreach (Base item in getRelatedObjects())
            {
                if (flags.Add(item))
                {
                    Q.Enqueue(item);
                }
            }
            while (Q.Count > 0)
            {
                Base obj = Q.Dequeue();
                if (!act(obj))
                {
                    break;
                }
                foreach (Base item in obj.getRelatedObjects())
                {
                    if (flags.Add(item))
                    {
                        Q.Enqueue(item);
                    }
                }
            }
        }
        public virtual void Paint(Graphics gra)
        {

        }
        public virtual void PaintVirtualObjects(Graphics gra)
        {

        }
        public virtual void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                NameDisplayer.Paint(gra);
            }
        }
        //public virtual Base[] GetSelectedObjects()
        //{
        //    return Selected ? new Base[] { this } : new Base[0];
        //}
        public virtual void Move(PointD location, bool isCompulsive = false, Base sender = null)
        {

        }
        /// <summary>
        /// 1.图形对元件创建时鼠标移动路径的限制；2.实时计算参数并显示在状态栏上；3.画笔类工具记录路径
        /// </summary>
        /// <param name="component">正在创建的元件</param>
        public virtual void ComponentBuilding(Base component)
        {

        }
        public virtual void CreateComponents()
        {
            if (LayerItem != null) Pad.Layer.Add(LayerItem);
            RefreshPropertyDisplayersText();
            RefreshPropertyDisplayersLocation();
            creationCompleted = true;
            Pad.Document.Modified = true;
            UIInteraction.StatusMessage.ShowMainStatus(Resources.Translate("Ready"));
            UIInteraction.StatusMessage.ClearSecondaryStatus();
        }
        public virtual Base CreateMirrorImage(double A, double B, double C)
        {
            return null;
        }
        public virtual void CheckInRegion(RectangleD rect)
        {

        }
        public virtual bool LocationUsed(IList<PointD> locations)
        {
            return false;
        }
        public virtual void RefreshPropertyDisplayersLocation()
        {
            NameDisplayer.RefreshLocation();
            PropertiesLocationManager.RegionRange = new RectangleD(AnchorPoint(NameDisplayer) + AnchorOffset, PropertiesLocationManager.RegionRange.Size);
        }
        public virtual void RefreshPropertyDisplayersText()
        {

        }
        public virtual void SetInPicker(bool value)
        {
            if (Flags.Ctrl)
            {
                RectangularPicker rp = Pad.ToolObj as RectangularPicker;
                if (rp == null)
                {
                    Selected = value;
                }
                else
                {
                    if (value)
                    {
                        Selected = !rp.GetInitialSelection(this);
                    }
                    else
                    {
                        Selected = rp.GetInitialSelection(this);
                    }
                }
            }
            else
            {
                Selected = value;
            }
            StyleState = Selected ? StyleState.Hover : StyleState.Normal;
        }
        public virtual void Relocate(PointsRelocationReason reason)
        {

        }
        public virtual void Delete(bool recoverable = false)
        {
            if (Pad.TrackObj == this) Pad.TrackObj = null;
            if (Pad.HostObj == this) Pad.HostObj = null;
            if (recoverable)
            {
                if (LayerItem != null)
                {
                    //LayerItem.ObjectState = ObjectState.Deleted;
                    LayerItem.ItemContainer.Visible = LayerItem.Selected_ = false;
                }
                if (RecycleItem == null)
                {
                    recycleItem = new ListViewItem(new string[] { Name, Resources.Translate("Component") }) { Tag = this };
                }
                foreach (PropertyInfo item in GetType().GetProperties())
                {
                    if (!typeof(DocStringX).IsAssignableFrom(item.PropertyType)) continue;
                    DocStringX dsx = item.GetValue(this, null) as DocStringX;
                    if (dsx is null) continue;
                    dsx.ForEachRelatedParameter((BasicParameter p) =>
                    {
                        p.ReferenceCount--;
                        return true;
                    });
                }
                UIInteraction.RecycleBin.Delete(this);
                ObjectState = ObjectState.Deleted;
            }
            else
            {
                if (LayerItem != null)
                {
                    LayerItem.Remove();
                }
                if (RecycleItem != null)
                {
                    RecycleItem.Remove();
                }
                foreach (PropertyInfo item in GetType().GetProperties())
                {
                    if (!typeof(DocStringX).IsAssignableFrom(item.PropertyType)) continue;
                    DocStringX dsx = item.GetValue(this, null) as DocStringX;
                    if (dsx is null) continue;
                    dsx.UnbindAllParameters();
                }
                ObjectState = ObjectState.Disposed;
            }
            foreach (ComponentManagerBase item in ComponentManagers)
            {
                if (item.ObjectState == ObjectState.Normal)
                {
                    item.Delete(recoverable);
                }
            }
            foreach (ObjectParameter item in ObjectParameters)
            {
                if (item.ObjectState == ObjectState.Normal)
                {
                    item.Delete(recoverable);
                }
            }
            Pad.Canvas.Invalidate();
        }
        public string OfTranslator(string sth)
        {
            return OfTranslator2(Resources.Translate(sth));
        }
        public string OfTranslator2(string sth)
        {
            return string.Format(Resources.Translate("Of"), Name, sth);
        }
        public virtual void CoordinateSystemTypeChanged()
        {
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate || ToolType == ToolTypeName.jzbx)
            {
                RotateCenter = new PPointS(Pad);
            }
            else
            {
                RotateCenter = new RPointS(Pad);
            }
            RotateCenter.SetFromPointD(Pad.ConvertRPointS(RotateCenter, Pad.OldCoordinate));
            NameDisplayer.CoordinateSystemTypeChanged();
        }
        public virtual void Recover()
        {
            if (IsTop && Pad.CheckObjectNameExist(Name))
            {
                DialogResult res = MessageBox.Show(string.Format(Resources.Translate("ComponentNameExistInSketchpad"), Name), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                switch (res)
                {
                    case DialogResult.Yes:
                        Name = Utility.GetNextAvailableName(Name, Pad.CheckObjectNameExist);
                        break;
                    case DialogResult.No:
                        return;
                    case DialogResult.Cancel:
                        throw new OperationCanceledException();
                }
            }
            foreach (PropertyInfo item in GetType().GetProperties())
            {
                if (!typeof(DocStringX).IsAssignableFrom(item.PropertyType)) continue;
                DocStringX dsx = item.GetValue(this, null) as DocStringX;
                if (dsx is null) continue;
                MultilingualDisplayNameAttribute[] attr = GetType().GetAttributes<MultilingualDisplayNameAttribute>(item.Name);
                dsx.SwitchDeletedParameters(attr?.Length > 0 ? attr[0].DisplayName : string.Empty);
            }
            if (LayerItem != null)
            {
                LayerItem.Selected_ = false;
                LayerItem.ItemContainer.Visible = true;
            }
            RecycleItem.Remove();
            //foreach (ComponentManagerBase item in ComponentManagers)
            //{
            //    item.Recover(arg);
            //}
            //foreach (ObjectParameter item in ObjectParameters)
            //{
            //    item.Recover(arg);
            //}
            ObjectState = ObjectState.Normal;
            Pad.Canvas.Invalidate();
        }
        public override string ToString()
        {
            return Name;
        }
        public virtual void UseFormat(Base obj)
        {
            if (Settings.Instance.TransformUseFormat)//顺序不可颠倒，因此不用反射
            {
                RotateCenter.UseFormat(obj.RotateCenter);
                RotateAngle.UseFormat(obj.RotateAngle);
            }
            Type t = GetType();
            foreach (PropertyInfo prop in t.GetProperties())
            {
                MultilingualCategoryAttribute[] attr = t.GetAttributes<MultilingualCategoryAttribute>(prop.Name);
                if (attr?.Length > 0)
                {
                    string cat = attr[0].OriginalCategory;
                    if (Settings.Instance.AppearanceUseFormat && cat == "Appearance" ||
                        Settings.Instance.PropertyDisplayerUseFormat && cat == "SketchpadDisplay" ||
                        Settings.Instance.TrackUseFormat && cat == "Track" ||
                        Settings.Instance.ViewUseFormat && cat == "View" ||
                        Settings.Instance.LayoutUseFormat && cat == "Layout")
                    {
                        if ((prop.Name == nameof(PointBase.OutlineColor) || prop.Name == nameof(PointBase.FillColor)) &&
                            ToolType != obj.ToolType || prop.Name == nameof(CustomBase.Location) ||
                            prop.Name == nameof(CustomBase.FitSize)) continue;
                        PropertyInfo pi = obj.GetType().GetProperty(prop.Name);
                        if (pi != null && pi.PropertyType == prop.PropertyType)
                        {
                            MethodInfo mi = prop.PropertyType.GetMethod(nameof(UseFormat));
                            if (mi == null)
                            {
                                prop.SetValue(this, pi.GetValue(obj, null), null);
                            }
                            else
                            {
                                mi.Invoke(prop.GetValue(this, null), new object[] { pi.GetValue(obj, null) });
                            }
                        }
                    }
                }
            }
        }
        public virtual void BeforePasting(Base src)
        {

        }
        public virtual object CopyAndPaste(SketchPad pad)
        {
            ConstructorInfo ci = GetType().GetConstructor(new Type[] { typeof(SketchPad), typeof(string), typeof(bool) });
            if (ci == null) return null;
            Base res = ci.Invoke(new object[] { pad, Utility.GetNextAvailableName(Name, pad.CheckObjectNameExist), LayerItem != null }) as Base;
            afterNewCopy(res);
            return res;
        }
        protected void afterNewCopy(Base copy)
        {
            copy.BeforePasting(this);
            copy.AutoCreate(GetCopyInfo(), true);
            copy.UseFormat(this);
        }
        public virtual void Flip(PointD location, double k)
        {

        }
        public void ShowItalicNameProperty()
        {
            NameDisplayer.Font = new Font("Times New Roman", 20, FontStyle.Bold | FontStyle.Italic);
            NameDisplayer.Visible = true;
        }
        public void SimulateDeleted(Action act)
        {
            if (act == null) return;
            ObjectState bkp = ObjectState;
            ObjectState = ObjectState.Deleted;
            act();
            ObjectState = bkp;
        }

        public void BeforeSerializing(Dictionary<string, object> data)
        {

        }

        public void AfterDeserialized(Dictionary<string, object> data)
        {
            if (ObjectState == ObjectState.Deleted)
            {
                recycleItem = new ListViewItem(new string[] { Name, Resources.Translate("Component") }) { Tag = this };
            }
        }
    }
}
